use actix_web::{HttpRequest, delete, get, post, put, web};

use log::{info, trace};
use rbase::{
    define::{ApiError, ApiResponse},
    id_utils::{id2str, str2id},
};
use serde::{Deserialize, Serialize};

use crate::{
    db::entities::user::Model,
    rest_api::service::{
        auth_check::get_user_from_request,
        srv_user::{self},
    },
};

use anyhow::Result;

#[derive(Clone, Debug, Deserialize)]
pub struct CreateParams {
    pub name: String,
    pub login_name: String,
}

#[derive(Clone, Debug, Deserialize)]
pub struct UpdateParams {
    pub name: String,
    pub login_name: String,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct User {
    pub id: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub login_name: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub created_at: Option<i64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub modified_at: Option<i64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub roles: Option<Vec<String>>,
}

impl User {
    pub fn from_model(entity: &Model) -> Self {
        User {
            id: id2str(entity.id),
            name: entity.name.clone(),
            login_name: entity.login_name.clone(),
            created_at: entity.created_at,
            modified_at: entity.modified_at,
            roles: None,
        }
    }

    pub fn to_model(&self) -> Result<Model> {
        Ok(Model {
            id: str2id(&self.id)?,
            name: self.name.clone(),
            login_name: self.login_name.clone(),
            created_at: self.created_at,
            modified_at: self.modified_at,
            ..Default::default()
        })
    }
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct RegisterParams {
    pub name: String,
    pub login_name: String,
    pub password: String,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct LoginParams {
    pub login_name: String,
    pub password: String,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct LoginRsp {
    pub token: String,
    pub user: User,
}

#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct UserToken {
    pub id: i64,
    pub roles: Vec<String>,
    pub timeout: i64,
    pub is_admin: bool,
    pub owner_ids: Option<Vec<i64>>,
}

#[derive(Deserialize, Debug)]
pub struct SearchParams {
    #[serde(default)]
    pub name: Option<String>,

    #[serde(default)]
    pub offset: Option<u64>,

    #[serde(default)]
    pub limit: Option<u64>,

    #[serde(default)]
    pub order_by: Option<String>,

    #[serde(default)]
    pub order: Option<String>,

    #[serde(default)]
    pub tag_ids: Option<Vec<String>>,
}

#[post("/api/v1/register")]
async fn register(req_body: String) -> Result<ApiResponse, ApiError> {
    let register_param: RegisterParams = serde_json::from_str(&req_body)?;
    let result = srv_user::register(&register_param).await?;
    Ok(ApiResponse::data(result))
}

#[post("/api/v1/login")]
async fn login(req_body: String) -> Result<ApiResponse, ApiError> {
    let login_param: LoginParams = serde_json::from_str(&req_body)?;
    let result = srv_user::login(&login_param).await?;
    Ok(ApiResponse::data(result))
}

#[get("/api/v1/users")]
async fn list_users(req: HttpRequest, query_params: web::Query<SearchParams>) -> Result<ApiResponse, ApiError> {
    let user_search_params = query_params.into_inner();
    trace!("list_users query: {:?}", user_search_params);
    let user_token = get_user_from_request(&req)?;
    let (users, total) = srv_user::search(&user_token, &user_search_params).await?;
    Ok(ApiResponse::datas(users, total))
}

#[post("/api/v1/users")]
async fn create_user(req: HttpRequest, req_body: String) -> Result<ApiResponse, ApiError> {
    trace!("create_user body: {}", req_body);
    let user_token = get_user_from_request(&req)?;
    let new_user: CreateParams = serde_json::from_str(&req_body)?;

    let user = srv_user::create_user(&user_token, &new_user).await?;
    Ok(ApiResponse::data(user))
}

#[get("/api/v1/users/{id}")]
async fn get_user(req: HttpRequest, path_params: web::Path<String>) -> Result<ApiResponse, ApiError> {
    let user_id = path_params.into_inner();
    trace!("get_user path: {:?}", user_id);
    let user_token = get_user_from_request(&req)?;
    let user = srv_user::find_user_by_id(&user_token, &user_id).await?;
    Ok(ApiResponse::data(user))
}

#[delete("/api/v1/users/{id}")]
async fn delete_user(req: HttpRequest, path_params: web::Path<String>) -> Result<ApiResponse, ApiError> {
    trace!("delete_user path: {:?}", path_params);
    let user_token = get_user_from_request(&req)?;
    let user_id = path_params.into_inner();
    srv_user::delete_user_by_id(&user_token, &user_id).await?;
    Ok(ApiResponse::success())
}

#[put("/api/v1/users/{id}")]
async fn update_user(req: HttpRequest, req_body: String, path_params: web::Path<String>) -> Result<ApiResponse, ApiError> {
    trace!("update_user path: {:?}, body: {}", path_params, req_body);
    let user_token = get_user_from_request(&req)?;
    let new_user: UpdateParams = serde_json::from_str(&req_body)?;

    let user_id = path_params.into_inner();
    srv_user::update_user_by_id(&user_token, &user_id, &new_user).await?;
    Ok(ApiResponse::success())
}

pub fn config(cfg: &mut web::ServiceConfig) {
    info!("Configuring user APIs");
    cfg.service(register);
    cfg.service(login);
    cfg.service(list_users);
    cfg.service(create_user);
    cfg.service(get_user);
    cfg.service(delete_user);
    cfg.service(update_user);
}
